package com.mymarkdown.app.common.lister.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.mymarkdown.app.common.lister.FileWatch;
import com.mymarkdown.app.common.lister.WatchFile;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author ：zenglun
 * @date ：Created in 2021/12/25 4:20 PM
 * @description：
 * @modified By：
 * @version: $
 */
@Slf4j
public class FileWatchImpl<T> extends Thread implements FileWatch<T> {
    protected AtomicBoolean isClose = new AtomicBoolean(true);


    protected Map<String, Set<String>> watchPathListItemMap = new ConcurrentHashMap<>();

    protected List<WatchFile> watchFileList = new CopyOnWriteArrayList<>();

    protected AtomicLong interval = new AtomicLong(3000);

    private boolean isStart;

    public FileWatchImpl() {
    }

    private void checkWatchPath(String watchPath) {
        if (StrUtil.isEmpty(watchPath)){
            throw new NullPointerException("要监听的路径为空!");
        }
        File watchFile = new File(watchPath);
        if (!watchFile.exists()){
            throw new RuntimeException("要监听的文件不存在!");
        }
    }

    private void watch() {
        //不断循环
        Set<Map.Entry<String, Set<String>>> entries = watchPathListItemMap.entrySet();
        Iterator<Map.Entry<String, Set<String>>> iterator = entries.iterator();

        Map.Entry<String, Set<String>> entry = null;
        File parentFile = null;
        while (iterator.hasNext()) {
            entry = iterator.next();
            String filepath = entry.getKey();
            Set<String> value = entry.getValue();
            parentFile = new File(filepath);
            File[] files = parentFile.listFiles();
            if (files != null && files.length > 0 && CollectionUtil.isNotEmpty(value)) {
                if (files.length != value.size()) {
                    // 找出被删除的文件并通知用户
                    Set<String> conCurrentFileList = Stream.of(files).map(File::getPath).collect(Collectors.toSet());

                    Iterator<String> it = value.iterator();
                    while (it.hasNext()){
                        String item = it.next();
                        if (!conCurrentFileList.contains(item)) {
                            // 通知该文件被删除了
                            this.watchFileList.forEach(watch -> {
                                log.info("[FileWatchImpl]---[watch],设置的监听路径为:路径:{}被删除!", item);
                                watch.watch(item, filepath, WatchFile.WatchFileType.REMOVE);
                            });
                            it.remove();
                        }
                    }
                }

                Stream.of(files).forEach(item -> {
                    String itemPath = item.getPath();
                    if (!value.contains(itemPath)) {
                        // 通知用户
                        //循环通知,给出当前itemPath,给出事件名称即可
                        this.watchFileList.forEach(watch -> {
                            log.info("[FileWatchImpl]---[watch],设置的监听路径为:路径:{}被添加!", itemPath);
                            watch.watch(itemPath, filepath, WatchFile.WatchFileType.ADD);
                        });
                        value.add(itemPath);
                    }
                });
            }
        }

    }


    @Override
    public void close() {
        isClose.set(false);
    }

    @Override
    public void addWatchPath(String path) {
        checkWatchPath(path);
        log.info("[FileWatchImpl]---[setWatchPath],设置的监听路径为:{}", path);
        Set<String> paths = Stream.of(Objects.requireNonNull(new File(path).listFiles())).map(File::getPath).collect(Collectors.toSet());
        this.watchPathListItemMap.put(path, paths);
    }

    @Override
    public void removeWatchPath(String path) {
        this.watchPathListItemMap.remove(path);
    }

    @Override
    public void addWatch(WatchFile watchFile) {
        if (Objects.isNull(watchFile)){
            throw new NullPointerException("要添加注册的监听器不能为空!");
        }
        this.watchFileList.add(watchFile);
    }

    @Override
    public void removeWatch(WatchFile watchFile) {
        this.watchFileList.remove(watchFile);
    }

    @Override
    public void setInterval(long interval) {
        this.interval.set(interval);
    }

    @Override
    public long getInterval() {
        return this.interval.get();
    }


    @Override
    public synchronized void start() {
        isStart = true;
        super.start();
    }

    @Override
    public boolean isStart() {
        return isStart;
    }

    @Override
    public void run() {
        while (isClose.get()) {
            this.watch();
            try {
                Thread.sleep(this.interval.get());
            } catch (InterruptedException e) {
                break;
            }
        }
    }

}
